﻿using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BLL
{
    public class Facturacao_BLL
    {
        #region singleton
        private static Facturacao_BLL instance;

        private Facturacao_BLL() { }

        public static Facturacao_BLL Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Facturacao_BLL();
                }
                return instance;
            }
        }
        #endregion

        public hrp_facturacao_geral ObterFacturaGeral(int id_factura, int empresaId)
        {
            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_geral.Where(f => f.id == id_factura && f.id_empresa==empresaId).FirstOrDefault();
        }

        public hrp_facturacao_geral ObterFacturaGeral(int id_cliente, int id_empresa, int mes, int ano)
        {
            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_geral.Where(f => f.mes == mes && f.id_cliente == id_cliente &&
                f.id_empresa == id_empresa && f.ano == ano && f.estado == 1).FirstOrDefault();
        }

        public hrp_facturacao_funcionario_detalhe ObterFacturaFuncionarioDetalhe(int id_funcionario, int id_factura_geral, int empresaId)
        {
            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_funcionario_detalhe.Where(f => f.id_funcionario == id_funcionario && f.id_facturacao_geral == id_factura_geral && f.id_empresa==empresaId).FirstOrDefault();
        }

        //Todo: um join a campeao!!
        public List<hrp_facturacao_funcionario_detalhe> ObterFacturasFuncionario(int id_funcionario, int empresaID)
        {
            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_funcionario_detalhe.Join(hrp.hrp_facturacao_geral.Where(factG => factG.estado == 1), f => f.id_facturacao_geral, factG => factG.id, (f, factG) => f).Where(f => f.id_funcionario == id_funcionario && f.id_empresa==empresaID).ToList();
        }


        public int criarFacturaGeral(int id_cliente, int id_empresa, int mes, int ano,
            int user_id, string ip, DateTime dataGeracao)
        {
            hrpEntities hrp = new hrpEntities();
            hrp_facturacao_geral factura = new hrp_facturacao_geral();
            factura.ano = ano;
            factura.estado = 0;
            factura.id_cliente = id_cliente;
            factura.id_empresa = id_empresa;
            factura.mes = mes;
            factura.total_funcionarios = 0;
            factura.user_id = user_id;
            factura.user_ip = ip;
            factura.user_timestamp = dataGeracao;
            factura.valor_total_irt = 0;
            factura.valor_total_salarios = 0;
            factura.valor_total_ss = 0;
            hrp.hrp_facturacao_geral.Add(factura);
            hrp.SaveChanges();
            return factura.id;
        }

        public bool ActualizarFacturaGeral(int facturaGeralId)
        {
            hrpEntities hrp = new hrpEntities();
            hrp_facturacao_geral factura = hrp.hrp_facturacao_geral.Where(f => f.id == facturaGeralId).FirstOrDefault();
            int totalFacturas = hrp.hrp_facturacao.Count(f => f.id_facturacao_geral == facturaGeralId);
            if (totalFacturas > 0)
            {
                var facturas = hrp.hrp_facturacao.Where(f => f.id_facturacao_geral == facturaGeralId);

                decimal totalSalarios = facturas.Sum(f => f.totalReceberLiquido);
                decimal totalIRT = facturas.Sum(f => f.total_desconto_irt);
                decimal totalSS = facturas.Sum(f => f.valor_desconto_ss);

                factura.total_funcionarios = facturas.Count();
                factura.valor_total_irt = totalIRT;
                factura.valor_total_salarios = totalSalarios;
                factura.valor_total_ss = totalSS;
                factura.estado = 1;
                hrp.SaveChanges();
                return true;
            }
            return false;
        }

        public void CancelarFacturaGeral(int facturaGeralId)
        {
            hrpEntities hrp = new hrpEntities();
            hrp_facturacao_geral factura = hrp.hrp_facturacao_geral.Where(f => f.id == facturaGeralId).FirstOrDefault();
            factura.estado = 2;
            hrp.SaveChanges();
        }

        public List<hrp_facturacao_geral> ListaFacturas(int id_cliente, int id_empresa, int estado)
        {

            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_geral.Where(f => f.estado == estado && f.id_cliente == id_cliente && f.id_empresa == id_empresa).OrderByDescending(f => f.ano).ThenByDescending(f => f.mes).ToList();
        }

        public List<hrp_facturacao_funcionario_detalhe> ListaFacturacaoDetalhada(int facturaId)
        {
            hrpEntities hrp = new hrpEntities();
            return hrp.hrp_facturacao_funcionario_detalhe.Where(f => f.id_facturacao_geral == facturaId).ToList();
        }

        public void gerarFactura(int facturaGeralId, int id_funcionario, int year,
            decimal salario, int mes, int user_id,
            int idcliente, string ip, int id_empresa,
            string moeda, decimal subs_chefia, decimal abono,
            decimal abono_familia, decimal subs_turno, decimal subs_transporte,
            decimal subs_assiduidade, decimal subs_desempenho, decimal subs_produtividade,
            decimal subs_alimentacao, DateTime dataGeracao, decimal sub10,
            decimal subExposicao, decimal subContagio, decimal subcompensacao,
            int numHorasSemana, int diasUteis, decimal ss, decimal sindicato, 
            decimal subsNatal, decimal SubsFerias)
        {
            hrpEntities hrp = new hrpEntities();
            /***********INICIO DA FACTURACAO: DATAS, SALARIOS BRUTOS E AFINS***********************/

            string data_init = year + "-" + mes + "-01";
            string data_fim = year + "-" + mes + "-" + DateTime.DaysInMonth(year, mes);
            
            DateTime datainicio = Convert.ToDateTime(data_init);
            DateTime datafim = Convert.ToDateTime(data_fim);

            //Obter contrato funcionario para comparar datas
            hrp_contratos funcionarioContrato = Funcionario_BLL.Instance.ObterContratoFuncionario(id_funcionario);
            if (funcionarioContrato == null) // caso nao tenha contrato nao pode ser processado o salario
                return;

            if (funcionarioContrato.data_fim < datainicio || funcionarioContrato.data_inicio > datafim)//verificar se ainda esta ao abrigo do contrato para o mes em questao
                return;

            decimal salarioBruto = salario;
            //decimal rendimentoHora = salarioBruto / numHorasSemana * 12 / 52;
            //decimal subFerias = rendimentoHora * (40 * 52 / 12) / diasUteis;
                       
            //decimal rendimentoDia = salarioBruto / 30;

            //usar estes 2 para calculo de salario e o descontos de IRT calcular sobre total de salario e total de abonos
            decimal rendimentoSemana1 =  salarioBruto * 12 / 52;
            decimal rendimentoHora1 = rendimentoSemana1 / 44;
            decimal rendimentoDia1 = rendimentoHora1 * 8;
            
            decimal rendimentoHora = rendimentoDia1 / 8;

            //calculo para as faltas rever ja estou cansado para melhorar isto por hj - estamos a duplicar trb aqui
            decimal totalAjudasParaFaltas = subs_chefia + subs_turno + subs_transporte + subs_assiduidade + 
                subs_desempenho + subs_produtividade + sub10 + subExposicao + subcompensacao + subContagio;

            decimal rendimentoSemanaFaltas = (salarioBruto + totalAjudasParaFaltas) * 12 / 52;
            decimal rendimentoHoraFaltas = rendimentoSemanaFaltas / 44;
            decimal rendimentoDiaFaltas = rendimentoHoraFaltas * 8;

          /*  decimal rendimento_HoraFaltasDia = rendimentoDiaFaltas / 8;*/
            


            int totalDiasTrab = 30;
            bool salarioNormal = datainicio >= funcionarioContrato.data_inicio && datafim <= funcionarioContrato.data_fim;
            
            // VERIFICAR Q MERDA E ESTA LOL
            if (!salarioNormal) // falta pensar no acerto de contas caso seja fim de contrato
            {
                totalDiasTrab = Math.Min(datafim.Day, ((DateTime)funcionarioContrato.data_fim).Day) - Math.Max(datainicio.Day, ((DateTime)funcionarioContrato.data_inicio).Day);
                salario = Convert.ToDecimal(Convert.ToInt32(salarioBruto * totalDiasTrab / 30));
                salarioBruto = salario;
            }
            
            /***********FALTAS***********************/

            //apanhar as faltas justificadas
            double totalFaltasJustificadas = Falta_BLL.Instance.obterFaltasCompletasFuncionario(id_funcionario, datainicio, datafim, true) +
                 (Falta_BLL.Instance.obterFaltasMeiasFuncionario(id_funcionario, datainicio, datafim, true) / 2);

            decimal valorFaltasJustificadas = 0; // todo: verificar se e realmente assim
           
            //apanhar as faltas injustificadas do funcionario
            int faltascompletas = Falta_BLL.Instance.obterFaltasCompletasFuncionario(id_funcionario, datainicio, datafim, false);
            int meiasFaltas = Falta_BLL.Instance.obterFaltasMeiasFuncionario(id_funcionario, datainicio, datafim, false);
           
            decimal valordeFaltasInjustificadas = 0;
            if (faltascompletas > 0)
                valordeFaltasInjustificadas += (rendimentoDiaFaltas * faltascompletas);

            if (meiasFaltas > 0)
                valordeFaltasInjustificadas += (rendimentoDiaFaltas * meiasFaltas / 2);



            /***********HORAS EXTRAS***********************/
            int horaExtra50 = 0;
            int horaExtra1 = 0;
            int horaExtra75 = 0;
            decimal valorHoras50 = 0;
            decimal valorHoras75 = 0;
            decimal valorHoras1 = 0;

            foreach (var item in HorasExtras_BLL.Instance.horasExtraFuncionarioMes(id_funcionario, datainicio, datafim))
            {
                if (item.valor == 1)
                    horaExtra1 += item.numeroHoras;

                if (item.valor == 0.50)
                    horaExtra50 += item.numeroHoras;

                if (item.valor == 0.75)
                    horaExtra75 += item.numeroHoras;
            }

            valorHoras50 = rendimentoHora * horaExtra50;
            valorHoras75 = rendimentoHora * horaExtra75;
            valorHoras1 = rendimentoHora * horaExtra1;
            
            /***********TRIBUTACAO ***********************/

            salario = salario + valorHoras50 + valorHoras75 + valorHoras1 - valordeFaltasInjustificadas;
            //ver onde esta colocado na tabela

            decimal totalAjudas = subs_chefia + abono_familia + abono + subs_turno +
                                    subs_transporte + subs_assiduidade + subs_desempenho +
                                    subs_produtividade + subs_alimentacao +
                                    sub10 + subExposicao + subcompensacao + subContagio;

            //para a ss, abono familia, subs alimentacao nao descontam
            decimal descontoSS = (salario + totalAjudas - abono_familia - subs_alimentacao) * ss;
            
            /*total de salario q desconta para IRT, nao engloba abono familia e retira-se a parte que 
            ja se descontou para SS para nao tributar duplamente*/
            decimal SalarioTotalSemSS = (salario + totalAjudas - abono_familia) - descontoSS;
            
            var tributacao = (from tributacoes in hrp.hrp_tributacao
                              where (tributacoes.parcela1 - (decimal)SalarioTotalSemSS <= 0) && (tributacoes.parcela2 - (decimal)SalarioTotalSemSS >= 0)
                             select tributacoes).FirstOrDefault();

            decimal perc_valor_variavel =Convert.ToDecimal((tributacao.valor_variavel.Replace(".",",")));

            decimal salario_parte_descontar = ((SalarioTotalSemSS) - ((decimal)tributacao.ate_excesso)) * perc_valor_variavel;
            decimal salario_parte_fixa_desconto = (decimal)tributacao.valor_fixo;
            
            decimal descontoIRT = salario_parte_descontar + salario_parte_fixa_desconto;
           // decimal descontoSS = salario * ss;

            decimal adiantamentos = 0;
            int num_adiantamento = hrp.hrp_adiantamentos.Count(adiantamento => adiantamento.estado == true && adiantamento.mes == mes && adiantamento.ano == year && adiantamento.id_funcionario == id_funcionario);
            if (num_adiantamento > 0)
                adiantamentos = hrp.hrp_adiantamentos.Where(adiantamento => adiantamento.estado == true && adiantamento.mes == mes && adiantamento.ano == year && adiantamento.id_funcionario == id_funcionario).Sum(a => a.valor);

            decimal sindicatos = sindicato;
           
            decimal descontosAplicar = valorFaltasJustificadas + descontoSS + descontoIRT + adiantamentos + sindicatos;
            
            decimal salarioLiquidoTributado = salario - descontosAplicar;
            
            /***********GRAVAR FACTURA NA BD***********************/

            hrp_facturacao gerarFactura = new hrp_facturacao();
            //dados gerais
            gerarFactura.processado = false;
            gerarFactura.id_empresa = id_empresa;
            gerarFactura.id_cliente = idcliente;
            gerarFactura.id_facturacao_geral = facturaGeralId;
            gerarFactura.user_id = user_id;
            gerarFactura.user_timestamp = dataGeracao;
            gerarFactura.user_ip = ip;
            gerarFactura.id_funcionario = id_funcionario;
            gerarFactura.salarioBrutoCFaltas = salario; /// ???
            gerarFactura.mes_referente = mes.ToString();
            gerarFactura.ano_referente = year.ToString();
            gerarFactura.moeda = moeda;
            gerarFactura.total_dias_facturados = totalDiasTrab;
            //ajudas
            gerarFactura.subs_chefia = subs_chefia;
            gerarFactura.subs_abonofamilia = abono_familia;
            gerarFactura.subs_abono = abono;
            gerarFactura.subs_turno = subs_turno;
            gerarFactura.subs_transporte = subs_transporte;
            gerarFactura.subs_assiduidade = subs_assiduidade;
            gerarFactura.subs_desempenho = subs_desempenho;
            gerarFactura.subs_produtivade = subs_produtividade;
            gerarFactura.subs_alimentacao = subs_alimentacao;
            gerarFactura.sub10 = sub10;
            gerarFactura.subs_exposicao = subExposicao;
            gerarFactura.subs_compensacao = subcompensacao;
            gerarFactura.subs_contagio = subContagio;
            gerarFactura.sub_ferias = SubsFerias;
            gerarFactura.sub_natal = subsNatal;
            

            //horas
            gerarFactura.horasExtra50 = horaExtra50;
            gerarFactura.horasExtra75 = horaExtra75;
            gerarFactura.horasExtra100 = horaExtra1;
            gerarFactura.valorHorasExtra50 = valorHoras50;
            gerarFactura.valorHorasExtra75 = valorHoras75;
            gerarFactura.valorHorasExtra100 = valorHoras1;

            //faltas
            gerarFactura.faltas_justificadas = totalFaltasJustificadas;
            gerarFactura.faltas_injustificadas = faltascompletas + meiasFaltas;
            gerarFactura.valor_faltas_justificadas = valorFaltasJustificadas;
            gerarFactura.valor_faltas_injustificadas = valordeFaltasInjustificadas;
            gerarFactura.valorDescontoDeFaltas = valordeFaltasInjustificadas + valorFaltasJustificadas;
            
            //tributacao e liquidez
            gerarFactura.irt_percentual = perc_valor_variavel* 100 +"%";
            gerarFactura.valor_desconto_fixo = (decimal)tributacao.valor_fixo;
            gerarFactura.valor_desconto_percentual = salario_parte_descontar;
            gerarFactura.seguranca_social_percentual = ss * 100 + "%";
            gerarFactura.salarioBruto = Convert.ToInt32(salarioBruto); // sera int???
            gerarFactura.total_desconto_irt = descontoIRT;
            gerarFactura.valor_desconto_ss = descontoSS;
            gerarFactura.valor_adiantamento = adiantamentos;
            gerarFactura.valor_sindicato = sindicatos;
            gerarFactura.salarioLiquido = salarioLiquidoTributado;
            gerarFactura.salarioLiquido_IRT = salario - descontoIRT;
            gerarFactura.SalarioLiquido_SS = salario - descontoSS;
            gerarFactura.salarioLiquido_IRT_SS = salario - descontoIRT - descontoSS;

            //resumo
            gerarFactura.total_abonos = salario + totalAjudas;
            gerarFactura.total_descontos = descontosAplicar;
            gerarFactura.totalReceberLiquido = salarioLiquidoTributado + totalAjudas + subsNatal+SubsFerias;


            hrp.hrp_facturacao.Add(gerarFactura);
            hrp.SaveChanges();
        }

    }
}
